﻿using System.Windows.Media;
using CommonBehaviors.Actions;
using Styx;
using Styx.Common;
using Styx.CommonBot;
using Styx.CommonBot.Frames;
using Styx.Plugins;
using Styx.TreeSharp;
using Styx.WoWInternals;
using Styx.WoWInternals.WoWObjects;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using System.Xml.Serialization;
using Action = Styx.TreeSharp.Action;
using Sequence = Styx.TreeSharp.Sequence;

namespace EpicJunkbox
{
    public class MainForm : Form
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private readonly System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this._cbEpic = new System.Windows.Forms.CheckBox();
            this._cbBlue = new System.Windows.Forms.CheckBox();
            this._cbGreen = new System.Windows.Forms.CheckBox();
            this._cbWhite = new System.Windows.Forms.CheckBox();
            this._cbGrey = new System.Windows.Forms.CheckBox();
            this._cbUnknown = new System.Windows.Forms.CheckBox();
            this._saveButton = new System.Windows.Forms.Button();
            this._cbMoney = new System.Windows.Forms.CheckBox();
            this._clrBlkLstBtn = new System.Windows.Forms.Button();
            this.SuspendLayout();
            // 
            // cbEpic
            // 
            this._cbEpic.AutoSize = true;
            this._cbEpic.Location = new System.Drawing.Point(12, 12);
            this._cbEpic.Name = "_cbEpic";
            this._cbEpic.Size = new System.Drawing.Size(76, 17);
            this._cbEpic.TabIndex = 0;
            this._cbEpic.Text = "Loot Epics";
            this._cbEpic.UseVisualStyleBackColor = true;
            // 
            // cbBlue
            // 
            this._cbBlue.AutoSize = true;
            this._cbBlue.Location = new System.Drawing.Point(12, 35);
            this._cbBlue.Name = "_cbBlue";
            this._cbBlue.Size = new System.Drawing.Size(76, 17);
            this._cbBlue.TabIndex = 0;
            this._cbBlue.Text = "Loot Blues";
            this._cbBlue.UseVisualStyleBackColor = true;
            // 
            // cbGreen
            // 
            this._cbGreen.AutoSize = true;
            this._cbGreen.Location = new System.Drawing.Point(12, 58);
            this._cbGreen.Name = "_cbGreen";
            this._cbGreen.Size = new System.Drawing.Size(84, 17);
            this._cbGreen.TabIndex = 0;
            this._cbGreen.Text = "Loot Greens";
            this._cbGreen.UseVisualStyleBackColor = true;
            // 
            // cbWhite
            // 
            this._cbWhite.AutoSize = true;
            this._cbWhite.Location = new System.Drawing.Point(117, 12);
            this._cbWhite.Name = "_cbWhite";
            this._cbWhite.Size = new System.Drawing.Size(83, 17);
            this._cbWhite.TabIndex = 0;
            this._cbWhite.Text = "Loot Whites";
            this._cbWhite.UseVisualStyleBackColor = true;
            // 
            // cbGrey
            // 
            this._cbGrey.AutoSize = true;
            this._cbGrey.Location = new System.Drawing.Point(117, 35);
            this._cbGrey.Name = "_cbGrey";
            this._cbGrey.Size = new System.Drawing.Size(77, 17);
            this._cbGrey.TabIndex = 0;
            this._cbGrey.Text = "Loot Greys";
            this._cbGrey.UseVisualStyleBackColor = true;
            // 
            // cbUnknown
            // 
            this._cbUnknown.AutoSize = true;
            this._cbUnknown.Location = new System.Drawing.Point(117, 58);
            this._cbUnknown.Name = "_cbUnknown";
            this._cbUnknown.Size = new System.Drawing.Size(96, 17);
            this._cbUnknown.TabIndex = 0;
            this._cbUnknown.Text = "Loot Unknown";
            this._cbUnknown.UseVisualStyleBackColor = true;
            // 
            // saveButton
            // 
            this._saveButton.Location = new System.Drawing.Point(152, 114);
            this._saveButton.Name = "_saveButton";
            this._saveButton.Size = new System.Drawing.Size(61, 23);
            this._saveButton.TabIndex = 1;
            this._saveButton.Text = "Save";
            this._saveButton.UseVisualStyleBackColor = true;
            this._saveButton.Click += new System.EventHandler(this.saveButton_Click);
            // 
            // cbMoney
            // 
            this._cbMoney.AutoSize = true;
            this._cbMoney.Location = new System.Drawing.Point(12, 81);
            this._cbMoney.Name = "_cbMoney";
            this._cbMoney.Size = new System.Drawing.Size(82, 17);
            this._cbMoney.TabIndex = 2;
            this._cbMoney.Text = "Loot Money";
            this._cbMoney.UseVisualStyleBackColor = true;
            // 
            // clrBlkLstBtn
            // 
            this._clrBlkLstBtn.Location = new System.Drawing.Point(12, 114);
            this._clrBlkLstBtn.Name = "_clrBlkLstBtn";
            this._clrBlkLstBtn.Size = new System.Drawing.Size(84, 23);
            this._clrBlkLstBtn.TabIndex = 3;
            this._clrBlkLstBtn.Text = "Clear Blacklist";
            this._clrBlkLstBtn.UseVisualStyleBackColor = true;
            this._clrBlkLstBtn.Click += new System.EventHandler(this.clrBlkLstBtn_Click);
            // 
            // MainForm
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(226, 149);
            this.Controls.Add(this._clrBlkLstBtn);
            this.Controls.Add(this._cbMoney);
            this.Controls.Add(this._saveButton);
            this.Controls.Add(this._cbUnknown);
            this.Controls.Add(this._cbGrey);
            this.Controls.Add(this._cbWhite);
            this.Controls.Add(this._cbGreen);
            this.Controls.Add(this._cbBlue);
            this.Controls.Add(this._cbEpic);
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
            this.MaximizeBox = false;
            this.Name = "MainForm";
            this.Text = "EpicJunkbox";
            this.Load += new System.EventHandler(this.MainForm_Load);
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private Button _saveButton;
        private CheckBox _cbEpic;
        private CheckBox _cbBlue;
        private CheckBox _cbGreen;
        private CheckBox _cbWhite;
        private CheckBox _cbGrey;
        private CheckBox _cbUnknown;
        private CheckBox _cbMoney;
        private Button _clrBlkLstBtn;

        public static MainForm Instance;

        public MainForm()
        {
            Instance = this;
            InitializeComponent();
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            _cbEpic.Checked = Settings.Instance.LootEpic;
            _cbBlue.Checked = Settings.Instance.LootBlue;
            _cbGreen.Checked = Settings.Instance.LootGreen;
            _cbWhite.Checked = Settings.Instance.LootWhite;
            _cbGrey.Checked = Settings.Instance.LootGrey;
            _cbUnknown.Checked = Settings.Instance.LootUnknown;
            _cbMoney.Checked = Settings.Instance.LootMoney;
        }

        private void saveButton_Click(object sender, EventArgs e)
        {
            Settings.Instance.LootEpic = _cbEpic.Checked;
            Settings.Instance.LootBlue = _cbBlue.Checked;
            Settings.Instance.LootGreen = _cbGreen.Checked;
            Settings.Instance.LootWhite = _cbWhite.Checked;
            Settings.Instance.LootGrey = _cbGrey.Checked;
            Settings.Instance.LootUnknown = _cbUnknown.Checked;
            Settings.Instance.LootMoney = _cbMoney.Checked;
            Settings.Instance.Save();
            //this.Close();
        }

        private void clrBlkLstBtn_Click(object sender, EventArgs e)
        {
            EpicJunkbox.LockBoxBlackList.Clear();
        }

    }

    [Serializable]
    public class Settings
    {
        private static Settings _instance;

        public static Settings Instance
        {
            get { return _instance ?? (_instance = Load()); }
        }

        #region Settings Serialization

        public static string ConfigFileFormat = "EpicJunkbox_{0}.config";

        public static string ConfigFile
        {
            get { return string.Format(ConfigFileFormat, StyxWoW.Me.Name); }
        }

        public static string SavePath
        {
            get
            {
                string path = Process.GetCurrentProcess().MainModule.FileName;
                path = Path.GetDirectoryName(path);
                if(path == null) {
                    return null;
                }
                path = Path.Combine(path, @"Plugins\EpicJunkbox\settings");
                return path;
            }
        }

        private static XmlSerializer _serializer;

        private static XmlSerializer Serializer
        {
            get { return _serializer ?? (_serializer = new XmlSerializer(typeof(Settings))); }
        }

        public static Settings Load()
        {
            string path = SavePath;
            string file = Path.Combine(path, ConfigFile);

            try
            {
                using (var fStream = new FileStream(file, FileMode.Open, FileAccess.Read))
                {
                    return (Settings)Serializer.Deserialize(fStream);
                }
            }
            catch
            {
                return new Settings();
            }
        }

        public void Save()
        {
            string path = SavePath;
            string file = Path.Combine(path, ConfigFile);

            if (!Directory.Exists(path))
            {
                Directory.CreateDirectory(path);
            }

            try
            {
                using (var fStream = new FileStream(file, FileMode.Create, FileAccess.Write))
                {
                    Serializer.Serialize(fStream, this);
                }
            }
            catch (Exception e)
            {
                Logging.Write(Colors.Red, "Error saving EpicJunkbox settings");
                Logging.WriteException(Colors.Red, e);
            }
        }

        #endregion

        public bool LootEpic = true;
        public bool LootBlue = true;
        public bool LootGreen = true;
        public bool LootWhite = false;
        public bool LootGrey = false;
        public bool LootUnknown = false;
        public bool LootMoney = false;
    }

    public class EpicJunkbox : HBPlugin
    {
        public override string Name { get { return "EpicJunkbox"; } }
        public override string Author { get { return "eracer"; } }
        public override Version Version { get { return _version; } }
        private readonly Version _version = new Version(3, 2, 3, 0);
        public override bool WantButton { get { return true; } }
        public override string ButtonText { get { return "Settings"; } }
        public MainForm Gui = new MainForm();

        private static readonly Stopwatch Sw = new Stopwatch();
        //private readonly Styx.Common.Helpers.WaitTimer _updateTimer = Styx.Common.Helpers.WaitTimer.ThirtySeconds;
        public static List<ulong> LockBoxBlackList = new List<ulong>();
        //public class LockBoxItem
        //{
        //    public uint Entry { get; set; }
        //    public uint Level { get; set; }
        //}
        //public static List<LockBoxItem> LockBoxList = new List<LockBoxItem>();
        private static bool LootFrameIsOpen { get; set; }
        private Composite _root;
        private WoWItem _item;


        private static readonly Dictionary<uint, uint> LockBoxesList = new Dictionary<uint, uint> {
	    {16883, 70}, // Worn Junkbox (70)
	    {16884, 175}, // Sturdy Junkbox (175)
	    {16885, 250}, // Heavy Junkbox (250)
        {16882, 1}, // Battered Junkbox (1)
        {29569, 300}, // Strong Junkbox (300)
        {31952, 325}, // Khorium Lockbox (325)
        {43575, 350}, // Reinforced Junkbox (350)
        {43622, 375}, // Froststeel Lockbox (375)
        {43624, 400}, // Titanium Lockbox (400)
        {45986, 400}, // Tiny Titanium Lockbox (400)
        {4632, 1}, // Ornate Bronze Lockbox (1)
        {4633, 25}, // Heavy Bronze Lockbox (25)
        {4634, 70}, // Iron Lockbox (70)
        {4636, 125}, // Strong Iron Lockbox (125)
        {4637, 175}, // Steel Lockbox (175)
        {4638, 225}, // Reinforced Steel Lockbox (225)
        {5758, 225},  // Mithril Lockbox (225)
        {5759, 225},  // Thorium Lockbox (225)
        {5760, 225},  // Eternium Lockbox (225)
        {63349, 400}, // Flame-Scarred Junkbox (400)
        {68729, 425}, // Elementium Lockbox (425)
        {88165, 450}, // Vine-Cracked Junkbox (450)
        {88567, 450} // Ghost Iron Lockbox (450)
        };

        private static readonly Dictionary<uint, uint> SkeletonKeysList = new Dictionary<uint, uint> {
        {15869, 100}, // Silver Skeleton Key (100)
	    {15870, 150}, // Golden Skeleton Key (150)
	    {15871, 200}, // Truesilver Skeleton Key (200)
	    {15872, 275}, // Arcanite Skeleton Key (275)
        {43854, 375}, // Cobalt Skeleton Key (375)
        {43853, 400}, // Titanium Skeleton Key (400)
        {55053, 425}, // Obsidium Skeleton Key (425)
        {82960, 450}, // Ghostly Skeleton Key (450)
        {82354, 450} // Ghost Iron Key (450)
        };

        private static bool LootEpic
        {
            get { return Settings.Instance.LootEpic; }
        }
        private static bool LootBlue
        {
            get { return Settings.Instance.LootBlue; }
        }
        private static bool LootGreen
        {
            get { return Settings.Instance.LootGreen; }
        }
        private static bool LootWhite
        {
            get { return Settings.Instance.LootWhite; }
        }
        private static bool LootGrey
        {
            get { return Settings.Instance.LootGrey; }
        }
        private static bool LootUnknown
        {
            get { return Settings.Instance.LootUnknown; }
        }
        private static bool LootMoney
        {
            get { return Settings.Instance.LootMoney; }
        }

        public override void OnButtonPress()
        {
            Gui.ShowDialog();
        }

        private bool _reEnableAutolootSetting;

        private string AutolootSetting
        {
            get { return Lua.GetReturnVal<string>(string.Format("return GetCVar(\"autoLootDefault\")"), 0); }
        }

        public override void Initialize()
        {
            Lua.Events.AttachEvent("LOOT_OPENED", LootFrameOpenedHandler);
            Lua.Events.AttachEvent("LOOT_CLOSED", LootFrameClosedHandler);
            //disableAutoloot();
            //Styx.CommonBot.BotEvents.OnBotStop += botStop;
            //Styx.CommonBot.BotEvents.OnBotStart += botStart;
            base.Initialize();
        }

        //private void botStart(System.EventArgs args)
        //{
        //    disableAutoloot();
        //}

        //private void botStop(System.EventArgs args)
        //{
        //    restoreAutoloot();
        //}

        public override void Dispose()
        {
            Lua.Events.DetachEvent("LOOT_OPENED", LootFrameOpenedHandler);
            Lua.Events.DetachEvent("LOOT_CLOSED", LootFrameClosedHandler);
            Settings.Instance.Save();
            //restoreAutoloot();
            base.Dispose();
        }

        private void LootFrameClosedHandler(object sender, LuaEventArgs args) { LootFrameIsOpen = false; }

        private void LootFrameOpenedHandler(object sender, LuaEventArgs args) { LootFrameIsOpen = true; }

        public override void Pulse()
        {
            if (Sw.Elapsed.TotalSeconds > 30 || !Sw.IsRunning)
            {
                if (StyxWoW.Me.Combat ||
                    StyxWoW.Me.IsCasting ||
                    StyxWoW.Me.Mounted ||
                    StyxWoW.Me.IsDead ||
                    StyxWoW.Me.HasAura("Food") ||
                    StyxWoW.Me.HasAura("Drink") ||
                    Battlegrounds.IsInsideBattleground ||
                    StyxWoW.Me.HasAura(1784) || // Stealth
                    StyxWoW.Me.IsMoving) return;

                if (!LockBoxItems.Any()) // if we still don't have an item then start the 30 second timer
                {
                    RestoreAutoloot();
                    Sw.Reset();
                    Sw.Start();
                }
                else
                {
                    _item = LockBoxItems.FirstOrDefault();
                    if (!DisableAutoloot())
                    {
                        if (_root == null)
                            _root = CheckInventoryItems();
                        Tick(_root);
                    }
                }
            }
        }

        private static void Tick(Composite tree)
        {
            if (tree.LastStatus != RunStatus.Running)
                tree.Start(null);

            if (tree.Tick(null) != RunStatus.Running)
                tree.Stop(null);
        }

        private void Log(String fmt, params object[] args)
        {
            Log(Colors.DodgerBlue, fmt, args);
        }

        private void Log(Color color, String fmt, params object[] args)
        {
            String s = String.Format(fmt, args);
            Logging.Write(color, String.Format("[{0}]: {1}", Name, s));
        }

        private bool DisableAutoloot()
        {
            if (AutolootSetting.Equals("1")) // only turn it off if it was on
            {
                _reEnableAutolootSetting = true;
                Lua.DoString("SetCVar(\"autoLootDefault\", \"0\");");
                Log("Disabled \"Auto Loot\".");
                return true;
            }
            return false;
        }

        private void RestoreAutoloot()
        {
            if (_reEnableAutolootSetting) // only turn it back on if we disabled it
            {
                Lua.DoString("SetCVar(\"autoLootDefault\", \"1\");");
                Log("Enabled \"Auto Loot\".");
                _reEnableAutolootSetting = false;
            }
        }

        public static LocalPlayer Me
        {
            get
            {
                return StyxWoW.Me;
            }
        }

        public Composite CheckInventoryItems()
        {
            return new PrioritySelector(
                new Decorator(ret => !_item.IsOpenable,
                    new Sequence(

                        // Unlock item
                        new Action(delegate { UnlockBox(_item); Log("Unlocking {0} guid {1}", _item.Name, _item.Guid); }),

                        //// Wait for casting to start...
                //new WaitContinue(5, ret => Me.IsCasting, new ActionAlwaysSucceed()),

                        //// Wait for casting to complete...
                //new WaitContinue(10, ret => !Me.IsCasting, new ActionAlwaysSucceed()),

                        // Wait for up to 12 seconds for box to become openable...
                        new WaitContinue(12, ret => _item.IsOpenable, new ActionAlwaysSucceed()),

                        // since its still not openable blacklist it or we will keep trying to unlock it
                        new Action(delegate { if (!_item.IsOpenable) LockBoxBlackList.Add(_item.Guid); })
                    )
                ),
                new Decorator(ret => _item.IsOpenable,
                    new Sequence(

                        // Open the box
                        new Action(delegate { UseItem(_item); Log("Looting {0} guid {1}", _item.Name, _item.Guid); }),

                        // Wait for the loot frame to appear...
                        new WaitContinue(10, ret => LootFrameIsOpen, new ActionAlwaysSucceed()),

                        // Get the loot from the box...
                        new Action(delegate { GetLoot(); }),

                        // Wait for the loot frame to disappear...
                        new WaitContinue(10, ret => !LootFrameIsOpen, new ActionAlwaysSucceed()),

                        // Blacklist the box...
                        new Action(delegate { LockBoxBlackList.Add(_item.Guid); })

                    )
                )
            );
        }

        private void GetLoot()
        {
            //log("Looting...");
            for (int i = 0; LootFrameIsOpen && i < LootFrame.Instance.LootItems; i++)
            {
                var lootInfo = LootFrame.Instance.LootInfo(i);
                if ((LootEpic && lootInfo.LootRarity == LootRarity.Epic)
                    || (LootBlue && lootInfo.LootRarity == LootRarity.Blue)
                    || (LootGreen && lootInfo.LootRarity == LootRarity.Green)
                    || (LootWhite && lootInfo.LootRarity == LootRarity.White && lootInfo.LootQuantity > 0)
                    || (LootGrey && lootInfo.LootRarity == LootRarity.Grey && lootInfo.LootQuantity > 0)
                    || (LootUnknown && lootInfo.LootRarity == LootRarity.Unknown)
                    || (LootMoney && lootInfo.LootQuantity == 0)
                    )
                    LootFrame.Instance.Loot(i);
            }
            LootFrame.Instance.Close();
        }

        private void UnlockBox(WoWItem item)
        {
            if (SpellManager.HasSpell(1804) && LockBoxesList[item.Entry] <= (Me.Level*5)) // if box is not openable and we have the pick lock spell then unlock it
            {
                //log("Unlocking...");
                SpellManager.Cast(1804); // Pick Lock
                UseItem(item);
            }
            //else if (SkeletonKeys.Where(o => o.ItemInfo.Level >= item.ItemInfo.Level).Count() > 0)
            //{
            //    WoWItem key = SkeletonKeys.Where(o => o.ItemInfo.Level >= item.ItemInfo.Level).FirstOrDefault();
            else if ((SkeletonKeys.Any(o => SkeletonKeysList.ContainsKey(o.Entry) && SkeletonKeysList[o.Entry] >= LockBoxesList[item.Entry])))
            {
                WoWItem key = SkeletonKeys.FirstOrDefault(o => SkeletonKeysList[o.Entry] >= LockBoxesList[item.Entry]);
                if (key != null && key.Usable)
                {
                    UseItem(key);
                    UseItem(item);
                }
            }
            else
            {
                Log("Can't Unlock {0} guid {1}", item.Name, item.Guid);
                LockBoxBlackList.Add(item.Guid);
            }

        }

        private void UseItem(WoWItem item)
        {
            //log("Using {0} guid {1}", item.Name, item.Guid);
            item.Use();
        }

        public static IEnumerable<WoWItem> SkeletonKeys
        {
            get
            {
                try
                {
                    var ret = StyxWoW.Me.BagItems.Where(o => SkeletonKeysList.ContainsKey(o.Entry));
                    return ret;
                }
                catch (NullReferenceException)
                {
                    return new List<WoWItem>();
                }
            }
        }

        public static IEnumerable<WoWItem> LockBoxItems
        {
            get
            {
                try
                {
                    var ret = StyxWoW.Me.BagItems.Where(o => !LockBoxBlackList.Contains(o.Guid) && LockBoxesList.ContainsKey(o.Entry));
                    return ret;
                }
                catch (NullReferenceException)
                {
                    return new List<WoWItem>();
                }
            }
        }
    }

    //public class Custom
    //{
    //    string item; //will hold the item
    //    System.Timers.Timer timer; //will hanlde the expiry
    //    List<Custom> refofMainList; //will be used to remove the item once it is expired

    //    public Custom(string yourItem, int milisec, List<Custom> refOfList)
    //    {
    //        refofMainList = refOfList;
    //        item = yourItem;
    //        timer = new System.Timers.Timer(milisec);
    //        timer.Elapsed += new ElapsedEventHandler(Elapsed_Event);
    //        timer.Start();
    //    }

    //    private void Elapsed_Event(object sender, ElapsedEventArgs e)
    //    {
    //        timer.Elapsed -= new ElapsedEventHandler(Elapsed_Event);
    //        refofMainList.Remove(this);

    //    }
    //}
}
